package com.jse.jdbc;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.util.Collection;
import java.util.Map;
import java.util.ServiceLoader;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import com.jse.Fs;
import com.jse.Ioc;
import com.jse.Js;
import com.jse.Jse;
import com.jse.Lang;
import com.jse.Log;
import com.jse.json.Json;

public class Jdbc {

	final static Log log=Log.get("Jdbc");
	public static boolean showsql=true;
	public final static ScopedValue<Connection> CONNECITON=ScopedValue.newInstance();
	public static String type="mysql";
//	static {Runtime.getRuntime().addShutdownHook(new Thread(Jdbc::close));}//注册关闭方法}
	
	public static void start() throws Exception {
		if(!Ioc.BEANS.containsKey("dataSource"))initDataSource(Jse.conf.prifix("jdbc.dataSource."));
		if(!Ioc.BEANS.containsKey("dao")){
			Ioc.reg("dao",ServiceLoader.load(Dao.class).findFirst().orElse(new Dao(){}));
		}
	}
	public static void initDataSource(Object map) throws NamingException {
		var conf=Json.jsonObject(map);
		String type=conf.getString("type","");
		String url=conf.getString("url");
		DataSource ds=switch (type) {
			case ""->new JseDataSource(conf);
			case "jndi"->InitialContext.doLookup(url);
			default ->(DataSource)Js.execute(Fs.readString("jse","/META-INF/ds/"+type+".js"),conf);
		};
		Ioc.reg("dataSource",ds);
	}
	
	public static Connection getConnection(DataSource ds) {
		try {//boolean pingStatus = conn.isValid(1000);会发送mysql ping命令
			if(CONNECITON.orElse(null)!=null){return CONNECITON.get();}
			return ScopedValue.where(CONNECITON,ds.getConnection()).get(CONNECITON);
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static Connection getConnection() {
		return getConnection(Ioc.get("dataSource"));
	}
	
	/**
	 * 
	 *	隔离级别							事务		脏读		不可重复读取	幻影读取	隔离级别	<br/>
	 *	0,TRANSACTION_NONE				不支持	不適用	不適用		不適用	无		<br/>
	 *	2,TRANSACTION_READ_COMMITTED	支持		阻止		允许			允许		可重复读	<br/>
	 *	1,TRANSACTION_READ_UNCOMMITTED	支持		允许		允许			允许		读未提交	<br/>
	 *	4,TRANSACTION_REPEATABLE_READ	支持		阻止		阻止			允许		可重复读	<br/>
	 *	8,TRANSACTION_SERIALIZABLE		支持		阻止		阻止			阻止		串行化	<br/>
	 * @param level 事务级别
	 * @param fun 	执行函数
	 */
	public static void tx(int level,Runnable fun) {
		Connection conn=getConnection();
		try{
			conn.setAutoCommit(false);
			conn.setTransactionIsolation(level);
			ScopedValue.runWhere(CONNECITON,conn,fun);
		} catch (Exception e) {
			//TODO 事务的错误处理
			e.printStackTrace();
			rollback(conn);
		} finally {
			endTx(conn);
		}	
	}
	
	public static void beginTx(Connection c) {
		try {
		  c.setAutoCommit(false);
		}catch(SQLException e) {
		  throw new RuntimeException(e);
		}
	}
	
	public static void endTx(Connection c) {
		try {
		  c.commit();
		  c.setAutoCommit(true);
		  c.close();
		  c=null;
		}catch(SQLException e) {
		  throw new RuntimeException(e);
		}
	}
	
	public static void rollback(Connection c) {
	   try{
	      c.rollback();
	   }catch(SQLException e) {
	      throw new RuntimeException(e);
	   }
	}
	
	
	public static void printWarnings(SQLWarning warning)
		    throws SQLException {
		    if (warning != null) {
		        System.out.println("\n---Warning---\n");
		    while (warning != null) {
		        System.out.println("Message: " + warning.getMessage());
		        System.out.println("SQLState: " + warning.getSQLState());
		        System.out.print("Vendor error code: ");
		        System.out.println(warning.getErrorCode());
		        System.out.println("");
		        warning = warning.getNextWarning();
		    }
		}
	}
	
	public static void setParam(PreparedStatement stmt,Object...args) throws SQLException {
		for(int i = 0; i < args.length; i++) {
			var o=Lang.def(args[i],null);
			if(o==null) {
				stmt.setObject(i+1,null);
			}else if(o instanceof Collection||o instanceof Map||o.getClass().isArray()) {
				stmt.setObject(i+1,Json.toJson(o));
			}else {
				stmt.setObject(i+1,o);
			}
		}
	}
//	CHAR, VARCHAR, BLOB, TEXT, ENUM, and SET	java.lang.String, java.io.InputStream, java.io.Reader, java.sql.Blob, java.sql.Clob
//	FLOAT, REAL, DOUBLE PRECISION, NUMERIC, DECIMAL, TINYINT, SMALLINT, MEDIUMINT, INTEGER, BIGINT	java.lang.String, java.lang.Short, java.lang.Integer, java.lang.Long, java.lang.Double, java.math.BigDecimal
//	DATE, TIME, DATETIME, TIMESTAMP	java.lang.String, java.sql.Date, java.sql.Timestamp
	public static String sql(String sql,Pager page) {
		return sql.concat(" limit "+page.limit());
	}
	
	public static void close() {
		
	}

    public static ScopedValue<Connection> getCONNECITON() {
        return CONNECITON;
    }
}
